package analizador.sintactico;

import java.io.IOException;

import categoria.lexica.CatLexica;

import analizador.lexico.AnalizadorLexicoLengDescAlgoritmica;
import categoria.token.Token;

public class AnalizadorSintacticoLengDescAlgoritmica {

	private Token tact;

	private AnalizadorLexicoLengDescAlgoritmica analizadorLexico;

	public AnalizadorSintacticoLengDescAlgoritmica(
			AnalizadorLexicoLengDescAlgoritmica analizadorLexico) {
		this.analizadorLexico = analizadorLexico;
	}

	public void parse() throws IOException {
		tact = analizadorLexico.sigToken();
		recProg();
		rec(CatLexica.EOF);
	}

	private void recProg() throws IOException {
		System.out.println("REC Prog");
    	if (tokenActual(CatLexica.INT) | tokenActual(CatLexica.BOOLEAN)) {
			recDecs();
    		rec(CatLexica.AMPERSAND);
    		recInsts();
    	}
    	else if (tokenActual(CatLexica.IDENTIFICADOR, CatLexica.IF, CatLexica.DO)) {
    		recInsts();
    	}
    	else errorSintactico(CatLexica.INT, CatLexica.BOOLEAN, CatLexica.IDENTIFICADOR, CatLexica.IF, CatLexica.DO);
	}
	
	private void recDecs() throws IOException {
		System.out.println("REC Decs");
		recDec();
		recRDec();
	}
	
	private void recRDec() throws IOException {
		System.out.println("REC RDec");
		if (tokenActual(CatLexica.PUNTOCOMA)){
			rec(CatLexica.PUNTOCOMA);
			recDec();
			recRDec();
		}
		else {}	//Lambda
	}
	
	private void recDec() throws IOException {
		System.out.println("REC Dec");
		if(tokenActual(CatLexica.INT)){
			rec(CatLexica.INT);
			rec(CatLexica.IDENTIFICADOR);
		}
		else if(tokenActual(CatLexica.BOOLEAN)){
			rec(CatLexica.BOOLEAN);
			rec(CatLexica.IDENTIFICADOR);
		}
	}
	
	
	private void recInsts() throws IOException {
		System.out.println("REC Insts");
		recInst();
		recRInst();
	}	
	
	private void recRInst() throws IOException {
		System.out.println("REC RInst");
		if (tokenActual(CatLexica.PUNTOCOMA)){
			rec(CatLexica.PUNTOCOMA);
			recInst();
			recRInst();
		}
		else {}	//Lambda
	}
	
	private void recInst() throws IOException {
		System.out.println("REC Inst");
		if (tokenActual(CatLexica.IDENTIFICADOR)){ // IAsignacion
			recIAsig();
		}
		else if (tokenActual(CatLexica.IF)){ // IIF
			recIIF();
		}
		else if (tokenActual(CatLexica.DO)){ // IDO
			recIDO();
		}
	}
	
	private void recIAsig() throws IOException {
		System.out.println("REC IAsig");
		rec(CatLexica.IDENTIFICADOR);
		rec(CatLexica.ASIGNACION);
		recExp0();		
	}
	
	private void recIIF() throws IOException {
		System.out.println("REC IF");
		rec(CatLexica.IF);
		recCasos();
		rec(CatLexica.FI);
	}

	private void recIDO() throws IOException {	
		System.out.println("REC DO");
		rec(CatLexica.DO);
		recCasos();
		rec(CatLexica.OD);
	}
	
	private void recCasos() throws IOException {
		System.out.println("REC Casos");
		recCaso();
		recRCasos();
	}
	
	private void recRCasos() throws IOException {
		System.out.println("REC RCasos");
		if(tokenActual(CatLexica.CORCHETEAPERTURA)){
			rec(CatLexica.CORCHETEAPERTURA);
			rec(CatLexica.CORCHETECIERRE);
			recCaso();
			recRCasos();
		}
		else{}//lambda
	}
	
	private void recCaso() throws IOException {
		System.out.println("REC Caso");
		rec(CatLexica.CASE);
		recExp0();
		rec(CatLexica.FLECHA);
		recInsts();
	}
	
	private void recExp0() throws IOException {	
		recExp1();
		recRExp0();
	}
	
	private void recRExp0() throws IOException {	
		if(tokenActual(CatLexica.EQ)|tokenActual(CatLexica.NEQ)|tokenActual(CatLexica.GT)|tokenActual(CatLexica.GE)|
				tokenActual(CatLexica.LT)|tokenActual(CatLexica.LE)){
			recOpComparacion();
			recExp1();
		}
		else{}//lambda
	}
	
	private void recOpComparacion() throws IOException {
		if(tokenActual(CatLexica.EQ)) {
			rec(CatLexica.EQ);
		}
		else if(tokenActual(CatLexica.NEQ)) {
			rec(CatLexica.NEQ);
		}
		else if(tokenActual(CatLexica.GT)) {
			rec(CatLexica.GT);
		}
		else if(tokenActual(CatLexica.GE)) {
			rec(CatLexica.GE);
		}
		else if(tokenActual(CatLexica.LT)) {
			rec(CatLexica.LT);
		}
		else if(tokenActual(CatLexica.LE)) {
			rec(CatLexica.LE);
		}
    	else {
    		errorSintactico(CatLexica.EQ, CatLexica.NEQ, CatLexica.GT, CatLexica.GE, CatLexica.LT, CatLexica.LE);
		}
		
	}

	private void recExp1() throws IOException {	
		recExp2();
		recRExp1();
	}
	
	private void recRExp1() throws IOException {	
		if(tokenActual(CatLexica.MAS)|tokenActual(CatLexica.MENOS)|tokenActual(CatLexica.OR)){
			recOpAditivo();
			recExp2();
		}
		else{}//lambda
	}
	
	private void recOpAditivo() throws IOException {
		 if (tokenActual(CatLexica.MAS)) {
		rec(CatLexica.MAS);
	}
	else if (tokenActual(CatLexica.MENOS)) {
		rec(CatLexica.MENOS);
	}
	else if (tokenActual(CatLexica.OR)) {
		rec(CatLexica.OR);
	}
	else {
    	errorSintactico(CatLexica.MAS, CatLexica.MENOS, CatLexica.OR);
	}
		
	}

	private void recExp2() throws IOException {	
		recExp3();
		recRExp2();
	}
	
	private void recRExp2() throws IOException {	
		if(tokenActual(CatLexica.POR)|tokenActual(CatLexica.ENTRE)|tokenActual(CatLexica.AND)){
			recOpMultDivAnd();
			recExp3();
		}
		else{}//lambda
	}
	
	private void recOpMultDivAnd() throws IOException {
		 if (tokenActual(CatLexica.POR)) {
				rec(CatLexica.POR);
			}
			else if (tokenActual(CatLexica.ENTRE)) {
				rec(CatLexica.ENTRE);
			}
			else if (tokenActual(CatLexica.AND)) {
				rec(CatLexica.AND);
			}
			else {
		    	errorSintactico(CatLexica.MAS, CatLexica.MENOS, CatLexica.OR);
			}
		
	}

	private void recExp3() throws IOException {	
		if(tokenActual(CatLexica.MENOS)){
			rec(CatLexica.MENOS);
			recExp3();
		}
		else if(tokenActual(CatLexica.NOT)){
				rec(CatLexica.NOT);
				recExp3();
		}
		else{
			recExp4();
		}
	}
	
	private void recExp4() throws IOException {
		if (tokenActual(CatLexica.TRUE)) {
			rec(CatLexica.TRUE);
		} else if (tokenActual(CatLexica.FALSE)) {
			rec(CatLexica.FALSE);
		} else if (tokenActual(CatLexica.NUMERO)) {
			rec(CatLexica.NUMERO);
		} else if (tokenActual(CatLexica.IDENTIFICADOR)) {
			rec(CatLexica.IDENTIFICADOR);
		} else if (tokenActual(CatLexica.PARENTESISAPERTURA)) {
			rec(CatLexica.PARENTESISAPERTURA);
			recExp0();
			rec(CatLexica.PARENTESISCIERRE);			
		} else {
			errorSintactico(CatLexica.TRUE, CatLexica.FALSE, CatLexica.NUMERO,
					CatLexica.IDENTIFICADOR);
		}
	}
	

	private void rec(CatLexica cat) throws IOException {
		System.out.println("REC "+cat);
		if (tokenActual(cat)) {
			tact = sigToken();
		} else {
			errorSintactico(cat);
		}
	}

	private void errorSintactico(CatLexica... catsEsperadas) {
		System.err.print("(FILA " + tact.leeFila() + ", POSICION " + tact.leeCol() + ")"
				+ "ERROR SINTACTICO: Encontrado " + tact
				+ ". Se esperaba alguno de los siguientes elementos: ");
		for (CatLexica catEsperada : catsEsperadas) {
			System.err.print(catEsperada + " ");
		}
		System.err.println();
		System.exit(1);
	}

	private boolean tokenActual(CatLexica... cats) {
		System.out.println("esta en tokenActual");
		boolean encontrada = false;
		int i = 0;
		while (i < cats.length && !encontrada) {
			encontrada = tact.leeCategoria() == cats[i];
			System.out.println("tokenActual"+cats[i]);
			i++;
		}
		return encontrada;
	}

	private Token sigToken() throws IOException {
		return analizadorLexico.sigToken();
	}

}